package easyexcel

import (
	"github.com/xuri/excelize/v2"
	"io"
	"net/http"
	"strconv"
	"time"
)

type WriteExcelStream[T any] interface {

	// SheetName 配置工作簿名称
	SheetName(sheetName string) WriteExcelStream[T]

	// TimeFormat 配置时间格式化
	TimeFormat(timeFormat string) WriteExcelStream[T]

	// ColWidthConfig 列宽设置配置
	ColWidthConfig(startCol, endCol string, width float64) WriteExcelStream[T]

	// RowHeight 行高设置配置
	RowHeight(height float64) WriteExcelStream[T]

	// WriteLocal 数据写入excel，并保存到本地
	WriteLocal(fileName string, suppleBeans func() []T)

	// WriteResponse 数据写入excel，并输出到浏览器
	WriteResponse(response http.ResponseWriter, suppleBeans func() []T)

	// WriteNet 数据写入excel，并输出到网络中
	WriteNet(writer io.Writer, suppleBeans func() []T)
}

type beanToExcelStream[T any] struct {
	*easyExcel[T]
}

// SheetName 配置sheet name
func (te *beanToExcelStream[T]) SheetName(sheetName string) WriteExcelStream[T] {
	te.setSheetName(sheetName)
	return te
}

// TimeFormat 配置时间格式化
func (te *beanToExcelStream[T]) TimeFormat(timeFormat string) WriteExcelStream[T] {
	te.setTimeFormat(timeFormat)
	return te
}

// ColWidthConfig 列宽设置配置
func (te *beanToExcelStream[T]) ColWidthConfig(startCol, endCol string, width float64) WriteExcelStream[T] {
	te.setColWidthConfig(startCol, endCol, width)
	return te
}

// RowHeight 行高设置配置
func (te *beanToExcelStream[T]) RowHeight(height float64) WriteExcelStream[T] {
	te.setRowHeight(height)
	return te
}

// WriteLocal 数据写入excel到本地
func (te *beanToExcelStream[T]) WriteLocal(fileName string, suppleBeans func() []T) {
	if fileName == "" {
		panic("excel文件名不能为空")
	}

	file := te.write(suppleBeans)
	defer func() {
		if err := file.Close(); err != nil {
			panic(err)
		}
	}()

	// 根据指定路径保存文件
	if err := file.SaveAs(fileName); err != nil {
		panic(err)
	}
}

// WriteResponse 数据写入excel到浏览器
func (te *beanToExcelStream[T]) WriteResponse(response http.ResponseWriter, suppleBeans func() []T) {
	milliStr := strconv.FormatInt(time.Now().UnixMilli(), 10)
	response.Header().Set("Content-Disposition", "attachment; filename="+milliStr+".xlsx")
	response.Header().Set("Content-Type", "application/octet-stream")
	te.WriteNet(response, suppleBeans)
}

// WriteNet 数据写入excel到网络
func (te *beanToExcelStream[T]) WriteNet(writer io.Writer, suppleBeans func() []T) {
	if writer == nil {
		panic("writer must not be nil")
	}

	file := te.write(suppleBeans)
	defer func() {
		if err := file.Close(); err != nil {
			panic(err)
		}
	}()

	err := file.Write(writer)
	if err != nil {
		panic(err)
	}
}

func (te *beanToExcelStream[T]) write(suppleBeans func() []T) *excelize.File {
	file := excelize.NewFile()

	// 创建一个工作表
	sw, err := file.NewStreamWriter(te.sheetName)
	if err != nil {
		panic(err)
	}

	// 配置设置
	te.settingFile(file)

	// 设置单元格的表头
	copyTitles := make([]any, len(te.tagTitles))
	for i, tagTitle := range te.tagTitles {
		copyTitles[i] = tagTitle
	}
	err = sw.SetRow("A1", copyTitles)
	if err != nil {
		panic(err)
	}

	// 设置单元格的值
	rowId := 1
	beans := suppleBeans()
	for len(beans) > 0 {
		for _, bean := range beans {
			rowId++

			// 获取cell名称，excelize.CoordinatesToCellName(1, 1) // returns "A1", nil
			cell, err := excelize.CoordinatesToCellName(1, rowId)
			if err != nil {
				panic(err)
			}

			row := te.bean2Rows(file, bean, rowId)
			err = sw.SetRow(cell, row)
			if err != nil {
				panic(err)
			}
		}
		beans = suppleBeans()
	}

	err = sw.Flush()
	if err != nil {
		panic(err)
	}

	return file
}
